package com.osce.score.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import cn.hutool.poi.excel.ExcelReader;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.csce.domain.entity.score.ScoreSheet;
import com.csce.domain.entity.score.ScoreSheetStep;
import com.csce.domain.entity.score.vo.ScoreSheetStepAtom;
import com.doudou.core.container.ListUtil;
import com.doudou.core.io.IoUtil;
import com.doudou.core.lang.ObjUtil;
import com.doudou.core.number.DecimalUtil;
import com.doudou.core.str.StrUtil;
import com.doudou.core.throwable.GlobalException;
import com.doudou.minio.Minio;
import com.douodu.excel.hutool.HutoExcelUtil;
import com.douodu.excel.status.ImportStatus;
import com.osce.common.utils.DateUtils;
import com.osce.common.utils.poi.ExcelExp;
import com.osce.common.utils.poi.ExcelUtil;
import com.osce.interfaces.score.IScoreSheetService;
import com.osce.score.mapper.ScoreSheetMapper;
import org.apache.poi.ss.usermodel.PictureData;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 评分表管理Service业务层处理
 *
 * @author zzx
 * @since 2023-08-16
 */
@Service
public class ScoreSheetServiceImpl extends ServiceImpl<ScoreSheetMapper, ScoreSheet> implements IScoreSheetService {
    private static final Log log = LogFactory.get(IScoreSheetService.class);

    @Resource private Minio minio;

    /**
     * 查询评分表管理
     *
     * @param id 评分表管理主键
     * @return 评分表管理
     */
    @Override
    public ScoreSheet selectScoreSheetById(Long id) {
        return baseMapper.selectScoreSheetById(id);
    }

    /**
     * 查询评分表管理列表
     *
     * @param scoreSheet 评分表管理
     * @return 评分表管理
     */
    @Override
    public List<ScoreSheet> selectScoreSheetList(ScoreSheet scoreSheet) {
        return baseMapper.selectScoreSheetList(scoreSheet);
    }

    /**
     * 新增评分表管理
     *
     * @param scoreSheet 评分表管理
     * @return 结果
     */
    @Override
    @Transactional
    public int insertScoreSheet(ScoreSheet scoreSheet) {
        scoreSheet.setCreateTime(DateUtils.getNowDate());
        int i = baseMapper.insertScoreSheet(scoreSheet);
        if (i > 0) {
            batchInsertSheetStep(scoreSheet.getSteps(), scoreSheet.getId());
        }
        return i;
    }

    /**
     * 修改评分表管理
     *
     * @param scoreSheet 评分表管理
     * @return 结果
     */
    @Override
    @Transactional
    public int updateScoreSheet(ScoreSheet scoreSheet) {
        int i = baseMapper.updateScoreSheet(scoreSheet);
        if (i > 0) {
            batchDeleteSheetStepBySheetId(scoreSheet.getId());
            batchInsertSheetStep(scoreSheet.getSteps(), scoreSheet.getId());
        }
        return i;
    }

    /**
     * 批量删除评分表管理
     *
     * @param ids 需要删除的评分表管理主键
     * @return 结果
     */
    @Override
    public int deleteScoreSheetByIds(Long[] ids) {
        int i = baseMapper.deleteScoreSheetByIds(ids);
        if (i > 0) {
            batchDeleteSheetStepBySheetIds(ids);
        }
        return i;
    }

    /**
     * 删除评分表管理信息
     *
     * @param id 评分表管理主键
     * @return 结果
     */
    @Override
    public int deleteScoreSheetById(Long id) {
        int i = baseMapper.deleteScoreSheetById(id);
        if (i > 0) {
            batchDeleteSheetStepBySheetId(id);
        }
        return i;
    }

    /**
     * 批量导入，支持多个sheet
     *
     * @param stream excel流
     */
    @Override
    public String importExcel(InputStream stream, Long skillId) {
        /* 为了重复读取，先将 InputStream 转为 ByteArrayInputStream
         * 每次读取完后使用 reset 重新定位到开头位置 */
        ByteArrayInputStream newIn;
        try {
            newIn = IoUtil.toByteArrayInputStream(stream);
        } catch (IOException e) {
            throw new GlobalException("读取文件失败");
        }
        //导入状态
        ImportStatus importStatus = new ImportStatus();
        //获取reader
        ExcelReader reader = HutoExcelUtil.getReader(newIn);
        IoUtil.reset(newIn);
        //sheet列表
        List<Sheet> sheets = reader.getSheets();
        String title;//表名
        String question;//题目
        byte[] imageBytes;//图片
        List<ScoreSheetStep> datas;
        for (int i = 0; i < sheets.size(); i++) {
            try {
                Sheet sheet = sheets.get(i);
                //名称
                title = sheet.getSheetName();
                //第一行为题目，第二行为备注
                List<Object> titles = reader.setSheet(sheet.getSheetName()).read().get(0);
                question = StrUtil.toStringOrNull(titles.get(0));
                //读取图片
                Map<String, PictureData> pictures = HutoExcelUtil.readPictures(sheet.getWorkbook(), i);
                imageBytes = ListUtil.isNotEmpty(pictures) ? ListUtil.getFirst(pictures.values()).getData() : null;
                //获取列表元素，标题数两行
                datas = new ExcelUtil<>(ScoreSheetStep.class).importExcel(sheet.getSheetName(), newIn, 1);
                if (ObjUtil.isEmpty(datas)) {
                    throw new GlobalException("{} 内容为空",sheet.getSheetName());
                }
                //组装元素
                insertScoreSheet(parseScoreSheetByExcel(skillId, title, question,imageBytes, datas));
                importStatus.addSuccessMsg(i + 1, sheet.getSheetName());
            } catch (Exception e) {
                log.error(e);
                importStatus.addFailError(i + 1, e);
            }finally {
                IoUtil.reset(newIn);
            }
        }
        //释放资源
        IoUtil.close(stream, newIn);
        return importStatus.toHtmlString();
    }

    /**
     * 格式化excel数据，主要功能还是检查模板类型
     * 如果合并了单元格，只有合并的第一行是有值的
     * 如果没有顺序，则是基础模板
     *
     * @param title 标题
     * @param question 题目
     * @param imageBytes 图片字节码
     * @param datas 数据
     */
    private ScoreSheet parseScoreSheetByExcel(Long skillId, String title, String question,byte[] imageBytes,List<ScoreSheetStep> datas) throws Exception {
        //检查元素
        String orderFlag = null;
        boolean isOrder = false;
        for (ScoreSheetStep excelVo : datas) {
            if (StrUtil.isNotBlank(excelVo.getOrder())) {
                isOrder = true;//如果不是空，说明是顺序模版
                orderFlag = excelVo.getOrder();
            } else if (StrUtil.isNotBlank(orderFlag)) {//如果空了并且之前有值，将上一个的值赋值给本次对象
                excelVo.setOrder(orderFlag);
            }
            //设置是否需要分数字段
            excelVo.parseNeedScore();
        }
        //上传图片
        String imageUrl = ObjUtil.isNull(imageBytes) ? null : minio.uploadFile(imageBytes);
        ScoreSheet build = ScoreSheet.builder().name(title).stepName("步骤").skillId(skillId).question(question).
                model(isOrder ? "model_order_step" : "model_atom").
                isSplitScore("2").
                steps(datas).imageUrl(imageUrl).
                total(ListUtil.sumDoubleByKey(datas, "score")).build();
        return build;
    }

    /**
     * 批量导出
     */
    @Override
    public List<ExcelExp> selectScoreSheetExcelList(ScoreSheet scoreSheet) {
        List<ScoreSheet> scoreSheets = baseMapper.selectScoreSheetList(scoreSheet);
        List<ExcelExp> list = ListUtil.list(true);
        List<ScoreSheetStep> dataJson;
        for (ScoreSheet sheet : scoreSheets) {
            dataJson = baseMapper.selectScoreSheetStepListBySheetId(sheet.getId());//获取步骤
            BigDecimal score = DecimalUtil.newDecimal(ListUtil.sumDoubleByKey(dataJson, "score"));//计算总分
            if ("model_atom".equals(sheet.getModel())) {//原始模板
                List<ScoreSheetStepAtom> scoreSheetStepAtoms = BeanUtil.copyToList(dataJson, ScoreSheetStepAtom.class);
                scoreSheetStepAtoms.add(ScoreSheetStepAtom.builder().step("总分").score(score).build());
                list.add(ExcelExp.builder().fileName(sheet.getName()).dataset(scoreSheetStepAtoms).clazz(ScoreSheetStepAtom.class).build());
            } else {//顺序步骤模板
                dataJson.add(ScoreSheetStep.builder().order("总分").step("总分").score(score).build());
                list.add(ExcelExp.builder().fileName(sheet.getName()).dataset(dataJson).clazz(ScoreSheetStep.class).build());
            }
        }
        return list;
    }

    @Override
    public Map<Long, ScoreSheet> selectSheetMapBySkillId(Long skillId) {
        return baseMapper.selectSheetMapBySkillId(skillId);
    }

    @Override
    public String selectScoreSheetStrByIds(Long skillId) {
        Map<Long, ScoreSheet> sheetMap = baseMapper.selectSheetMapBySkillId(skillId);
        StringBuilder str = new StringBuilder();
        for (ScoreSheet value : sheetMap.values()) {
            str.append(value.getName()).append("、");
        }
        return StrUtil.sub(str, 0, str.length() - 1);
    }

    @Override
    public Map<Long, ScoreSheet> selectScoreSheetMapByIds(Collection<Long> sheetIds) {
        return baseMapper.selectScoreSheetMapByIds(sheetIds);
    }

    @Override
    public List<ScoreSheet> selectScoreSheetListByIds(Collection<Long> sheetList) {
        return baseMapper.selectScoreSheetListByIds(sheetList);
    }

    //=========================================

    /**
     * 批量导入评分表步骤
     */
    private int batchInsertSheetStep(List<ScoreSheetStep> steps, Long sheetId) {
        if (ListUtil.isEmpty(steps)) {
            return 0;
        }
        steps = steps.stream().peek(s -> s.setSheetId(sheetId)).collect(Collectors.toList());
        return baseMapper.batchInsertSheetStep(steps);
    }

    /**
     * 根据评分表编号批量删除评分表步骤
     */
    private int batchDeleteSheetStepBySheetId(Long sheetId) {
        return baseMapper.batchDeleteSheetStepBySheetId(sheetId);
    }

    private int batchDeleteSheetStepBySheetIds(Long[] sheetIds) {
        return baseMapper.batchDeleteSheetStepBySheetIds(sheetIds);
    }
}



















